﻿
namespace PhotoAlbum.Web.Services
{
    using System;
    using System.ComponentModel.DataAnnotations;
    using System.Data;
    using System.Linq;
    using System.ServiceModel.DomainServices.EntityFramework;
    using System.ServiceModel.DomainServices.Hosting;
    using System.ServiceModel.DomainServices.Server;
    using PhotoAlbum.Web;


    [EnableClientAccess()]
    public class AlbumsService : PhotoAlbumService
    {
        [Query(ResultLimit = 20, HasSideEffects = false, IsDefault = true)]
        public IQueryable<Album> GetLatestPublicAlbums()
        {
            return this.ObjectContext
                        .Albums
                        .Where(a => a.VisibilityStore == (int)AlbumVisibility.Public)
                        .OrderByDescending(a => a.DateCreated);
        }

        [Query(IsComposable = false)]
        public IQueryable<Album> GetAlbumByPictureId(int pictureId)
        {
            var picture = this.GetViewablePicture(pictureId);
            return new[] { picture.Album }.AsQueryable();
        }

        [RequiresAuthentication]
        public IQueryable<Album> GetAlbums()
        {
            return this.PhotoUser.Albums.CreateSourceQuery().OrderByDescending(a => a.DateCreated);
        }

        [RequiresAuthentication]
        [Query(ResultLimit = 20, HasSideEffects = false)]
        public IQueryable<Album> GetFriendsAlbums()
        {
            return this.ObjectContext.PhotoAlbumUsers
                            .Where(pu => pu.Friends.Any(f => f.Id == this.PhotoUser.Id))
                            .SelectMany(pu => pu.Albums.Where(a => a.VisibilityStore != (int)AlbumVisibility.Private))
                            .OrderByDescending(a => a.DateCreated);
        }

        [RequiresAuthentication]
        [Query(ResultLimit = 20, HasSideEffects = false)]
        public IQueryable<Album> GetInvitationalAlbums()
        {
            return this.ObjectContext.Albums
                              .Where(a => a.InvitedUsers.Contains(this.PhotoUser))
                              .OrderByDescending(a => a.DateCreated);
        }

        [RequiresAuthentication]
        public void InsertAlbum(Album album)
        {
            album.DateCreated = DateTime.Now;
            album.Owner = this.PhotoUser;
            album.ViewsCount = 0;

            if ((album.EntityState != EntityState.Detached))
            {
                this.ObjectContext.ObjectStateManager.ChangeObjectState(album, EntityState.Added);
            }
            else
            {
                this.ObjectContext.Albums.AddObject(album);
            }
        }

        [RequiresAuthentication]
        public void UpdateAlbum(Album currentAlbum)
        {
            var original = this.ChangeSet.GetOriginal(currentAlbum);
            if(currentAlbum.OwnerId != original.OwnerId)
            {
                throw new ValidationException("The owner of an album cannot be changed.");
            }
            this.ObjectContext.Albums.AttachAsModified(currentAlbum, original);
        }

        [RequiresAuthentication]
        public void DeleteAlbum(Album album)
        {
            var original = this.ChangeSet.GetOriginal(album);
            if (album.OwnerId != original.OwnerId)
            {
                throw new ValidationException("Cannot delete someone else's album.");
            }

            if ((album.EntityState == EntityState.Detached))
            {
                this.ObjectContext.Albums.Attach(album);
            }
            this.ObjectContext.Albums.DeleteObject(album);
        }

        [RequiresAuthentication]
        public IQueryable<Invitation> GetInvitations()
        {
            return this.PhotoUser.Invitations.CreateSourceQuery().OrderByDescending(i => i.DateCreated);
        }

        [Invoke]
        [RequiresAuthentication]
        public void InviteUserToAlbum([Required] string username, int albumId)
        {
            this.PhotoUser.Albums.Load();
            var album = this.PhotoUser.Albums.FirstOrDefault(a => a.Id == albumId);
            if (album == null)
            {
                throw new Exception("Only the owner of an album can invite users to it.");
            }

            var user = this.ObjectContext.PhotoAlbumUsers.FirstOrDefault(u => u.UserName == username);
            if(user == null)
            {
                throw new Exception("User with the given username not found.");
            }
            var invitation = new Invitation()
            {
                AlbumId = albumId,
                DateCreated = DateTime.UtcNow,
                UserId = user.Id,
            };
            this.ObjectContext.AddToInvitations(invitation);
        }

        [RequiresAuthentication]
        [Query(HasSideEffects = true)]
        public IQueryable<Album> AcceptInvitationId(int invitationId)
        {
            this.PhotoUser.Invitations.Load();
            var invitation = this.PhotoUser.Invitations.FirstOrDefault(i => i.Id == invitationId);
            if (invitation == null)
            {
                throw new Exception("An album invitation can only be accepted by its recepient.");
            }
            var album = this.ObjectContext.Albums.FirstOrDefault(a => a.Id == invitation.AlbumId);
            // The album may have been deleted, no exception here.
            if (album != null)
            {
                this.PhotoUser.PrivateAlbums.Add(album);
                this.ObjectContext.Invitations.DeleteObject(invitation);
                this.ObjectContext.SaveChanges();
            }
            return this.PhotoUser.PrivateAlbums.CreateSourceQuery();
        }

        [Invoke]
        [RequiresAuthentication]
        public void RejectInvitationId(int invitationId)
        {
            this.PhotoUser.Invitations.Load();
            var invitation = this.PhotoUser.Invitations.FirstOrDefault(i => i.Id == invitationId);
            if (invitation == null)
            {
                throw new Exception("An album invitation can only be rejected by its recepient.");
            }
            this.ObjectContext.Invitations.DeleteObject(invitation);
            this.ObjectContext.SaveChanges();
        }
    }
}


